package cn.edu.nuc.article.util;

import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Random;

/**
 * @ClassName: StringTools
 * @Description: 自定义字符串工具类
 * @since: 1.0.0
 * @author: dzy
 * @date: 2016年11月3日 下午3:08:39
 */
public class CustomStringUtils {

    /**
     * @Title: leftFill
     * @Description: 长度不足,左补字符
     * <p>用于要求定长字符串的,长度不足时左补字符串的场景.</p>
     * @since: 1.0.0
     * @param str    原字符串
     * @param ch    长度不足时要补的字符
     * @param len    定长字段的长度(要补够多长)
     * @return
     */
    public static String leftFill(String str, char ch, int len){
        if (null == str) {
            str = "";
        }

        int diff = len - str.length();
        if (0 < diff) {
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < diff; i++) {
                buf.append(ch);
            }
            buf.append(str);
            str = buf.toString();
        }

//        while (str.length() < len) {
//            str = ch + str;
//        }
        return str;
    }

    /**
     * @Title: rightFill
     * @Description: 长度不足,左补字符
     * <p>用于要求定长字符串的,长度不足时右补字符串的场景.</p>
     * @since: 1.0.0
     * @param str    原字符串
     * @param ch    长度不足时要补的字符
     * @param len    定长字段的长度(要补够多长)
     * @return
     */
    public static String rightFill(String str, char ch, int len){
        if (null == str) {
            str = "";
        }

        int diff = len - str.length();
        if (0 < diff) {
            StringBuffer buf = new StringBuffer();
            buf.append(str);
            for (int i = 0; i < diff; i++) {
                buf.append(ch);
            }
            str = buf.toString();
        }

//        while (str.length() < len) {
//            str = str + ch;
//        }
        return str;
    }

    /**
     * @Title: append
     * @Description: 拼接字符串
     * @since: 1.0.0
     * @param objs    需要拼接的对象
     * @return
     */
    public static String append(Object... objs) {
        if (null == objs || objs.length == 0) {
            return "";
        }
        StringBuffer buffer = new StringBuffer();
        for (Object obj : objs) {
            buffer.append(obj);
        }
        return buffer.toString();
    }

    /**
     * @Title: nullToBlank
     * @Description: 如果字符串为空(null)，则返回空字符
     * @since: 1.0.0
     * @param str
     * @return
     */
    public static String nullToBlank(String str){
        return str == null ? "" : str;
    }

    /**
     * @Title: convertMapToString
     * @Description: 将map转换成字符串
     * @since: 1.0.0
     * @param map        需要转字符串的Map对象
     * @param symbol    转成字符串之后键值对之间的间隔符号
     * @return String,如果symbol为& 则返回格式为:key1=value1&key2=value2...
     */
    public static String convertMapToString(Map<String, String> map, String symbol){
        if (null == map || map.isEmpty() || StringUtils.isEmpty(symbol)) {
            return null;
        }
        StringBuffer buffer = new StringBuffer();
        boolean first = true;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (first) {
                buffer.append(entry.getKey()).append("=").append(entry.getValue());
                first = false;
            } else {
                buffer.append(symbol).append(entry.getKey()).append("=").append(entry.getValue());
            }
        }
        return buffer.toString();

    }

    /**
     * @Title: convertMapToString
     * @Description: 将map转化成字符串，并对map.value进行URL编码（当参数值为null时，则忽略该参数）
     * @since: 1.0.0
     * @param map        需要转字符串的Map对象
     * @param symbol    转成字符串之后键值对之间的间隔符号
     * @param encoding    对map.value进行编码的编码格式(utf-8,gbk等)
     * @return
     */
    public static String convertMapToString(Map<String, String> map, String symbol, String encoding){
        if (map == null || map.isEmpty() || StringUtils.isEmpty(symbol)) {
            return null;
        }
        StringBuffer buffer = new StringBuffer();
        boolean first = true;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (first) {
                buffer.append(entry.getKey()).append("=").append(urlEncode(entry.getValue(), encoding));
                first = false;
            } else {
                buffer.append(symbol).append(entry.getKey()).append("=").append(urlEncode(entry.getValue(), encoding));
            }
        }
        return buffer.toString();
    }

    /**
     * @Title: urlEncode
     * @Description: 对字符串进行URL编码
     * @since: 1.0.0
     * @param str        需要编码的字符串
     * @param encoding    编码格式(utf-8,gbk等)
     * @return    String 编码后的字符串
     */
    public static String urlEncode(String str, String encoding) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        try {
            return URLEncoder.encode(str, encoding);
        } catch (UnsupportedEncodingException e) {
            System.err.println(append("URL encode string error. str=", str, ", encoding=", encoding) + e);
            return str;
        }
    }

    /**
     * @Title: urlDecode
     * @Description: 对字符串进行URL解码
     * @since: 1.0.0
     * @param str        需要解码的字符串
     * @param encoding    解码的编码(utf-8,gbk等)
     * @return
     */
    public static String urlDecode(String str, String encoding){
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        try {
            return URLDecoder.decode(str, encoding);
        } catch (UnsupportedEncodingException e) {
            System.err.println(append("URL decode string error. str=", str, ", encoding=", encoding) + e);
            return str;
        }
    }

    /**
     * @Title: generate
     * @Description: 随机生成指定长度的16进制的字符串
     * @since: 0.0.1
     * @param len    长度
     * @return
     */
    public static String generateRandomHex(int len){

        if (len < 1) {
            return null;
        }

        StringBuffer sb = new StringBuffer();
        Random random = new Random();

        for (int i = 0; i < len; i++) {
            sb.append(Integer.toHexString(random.nextInt(16)));
        }

        return sb.toString().toUpperCase();
    }

    /**
     * @Title: generateConformOddCheckHex
     * @Description: 生成符合奇校验的字符串
     * @since: 0.0.1
     * @param len    字节长度
     * @return
     */
    public static String generateConformOddCheckHex(int len) {

        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            Random random = new Random();
            int randomInt = random.nextInt(255);
            String binaryString = Integer.toBinaryString(randomInt);
            boolean oddCheck = isConformOddCheck(binaryString);
            if (!oddCheck) {
                randomInt ^= 1;
            }
            byte b = (byte) randomInt;
            bytes[i] = b;
        }
        String hexString = CommonUtils.byte2hexString(bytes);
        return hexString;
    }

    /**
     * @Title: oddCheck
     * @Description: 检验是否符合奇校验
     * @since: 0.0.1
     * @param binaryString
     * @return
     */
    private static boolean isConformOddCheck(String binaryString) {
        int sum = 0;
        char[] charArray = binaryString.toCharArray();
        for(char c : charArray){
            if (c == '1') {
                sum ++;
            }
        }
        if (sum%2 == 1) {
            return true;
        }
        return false;
    }


    public static String replaceStr(String str, int startIndex, String newPart) throws Exception {

        int length = str.length();
        if (startIndex < 0 || startIndex > length) {
            throw new RuntimeException("索引不在允许范围内.");
        }

        int newPartLen = newPart.length();
        int secondStartIndex = startIndex + newPartLen;

        if (secondStartIndex > length) {
            throw new RuntimeException("索引不在允许范围内或替换的新字段过长.");
        }
        String beforeStr = "";
        if (startIndex > 0) {
            beforeStr = str.substring(0, startIndex);
        }

        String afterStr = "";
        if (secondStartIndex < length-1) {
            afterStr = str.substring(secondStartIndex);
        }

        String newStr = append(beforeStr, newPart, afterStr);

        return newStr;
    }

}